﻿namespace Contoso.ActiveDirectory {

    #region Usings
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.DirectoryServices;
    using System.Threading;
    using System.Diagnostics;
    #endregion

    /// <summary>
    /// When querying for a Site, the base DN must be the Configuration Naming Context
    /// </summary>
    [Serializable]
    public class Site : ADBaseObjectProperties, IGroupPolicyAttributes {

        #region Members
        #region Fields
        public static readonly new string[] AttributesToGet;

        public static readonly string ObjectCategoryShortName = "Site";
        #endregion

        #region IGroupPolicyAttributes
        public string GPLink { get; private set; }

        public IReadOnlyList<string> GPLinkGPODNs { get; private set; }

        public string GPOptions { get; private set; }
        #endregion

        public InstanceTypeFlags InstanceType { get { return instanceType; } }
        private InstanceTypeFlags instanceType;

        public string Location { get; private set; }

        /// <summary>
        /// This object may not exist.  Ensure that it is checked for null.
        /// </summary>
        public NTDSSiteSettings NTDSSiteSettings {
            get {
                if (ntdsSiteSettings == null) {
                    var filter = string.Format("(objectCategory={0})", NTDSSiteSettings.ObjectCategoryShortName);
                    var ntdsSiteSettingsList = ADDataAccess.GetADObjectProperties<NTDSSiteSettings>(
                        this.DN, NTDSSiteSettings.AttributesToGet, filter, PartitionScope.LocalPartition);

                    ntdsSiteSettings = ntdsSiteSettingsList.FirstOrDefault();
                }

                return ntdsSiteSettings;
            }
        }
        private NTDSSiteSettings ntdsSiteSettings;

        /// <summary>
        /// The collection of subnets assigned to the Site.
        /// </summary>
        public IReadOnlyList<string> SiteObjectBL { get { return siteObjectBL; } }
        private List<string> siteObjectBL;

        public IReadOnlyList<SiteServer> SiteServers {
            get {
                if (siteServers == null) {
                    this.GetSiteServers();
                }

                return siteServers;
            }
        }
        private List<SiteServer> siteServers;
        #endregion

        #region Constructors
        static Site() {
            var attributesToGet = new List<string>();
            attributesToGet.AddRange(new string[]
            {
                "gpLink",
                "gpOptions",
                "instanceType",
                "location",
                "siteObjectBL"
            });

            attributesToGet.AddRange(ADBaseObjectProperties.AttributesToGet);

            AttributesToGet = attributesToGet.Distinct().ToArray();
            attributesToGet = null;
        }
        public Site() {
            this.Initialize();
        }

        public Site(DirectoryEntry directoryEntry, bool destroyInputObject = true)
            : base(directoryEntry, destroyInputObject) {
            if (this.GetType().Name == typeof(Site).Name) {
                this.Attributes = null;
            }
        }

        public Site(SearchResult result, bool destroyInputObject = true)
            : base(result, destroyInputObject) {
            if (this.GetType().Name == typeof(Site).Name) {
                this.Attributes = null;
            }
        }

        public Site(List<string> ldifOutput)
            : base(ldifOutput) {
            if (this.GetType().Name == typeof(Site).Name) {
                this.Attributes = null;
            }
        }
        #endregion

        #region Methods

        public void GetSiteServers() {
            var filter = string.Format("(objectCategory={0})", SiteServer.ObjectCategoryShortName);
            siteServers = ADDataAccess.GetADObjectProperties<SiteServer>(
                this.DN, SiteServer.AttributesToGet, filter, PartitionScope.LocalPartition);

            if (siteServers == null) siteServers = new List<SiteServer>();
        }

        protected override void Initialize() {
            base.Initialize();

            instanceType = InstanceTypeFlags.Undefined;
            this.Location = string.Empty;

            // IGroupPolicyAttributes
            this.GPLink = string.Empty;
            this.GPLinkGPODNs = new List<string>();
            this.GPOptions = string.Empty;
            this.siteObjectBL = new List<string>();
        }

        protected override void ProcessResults() {

            try {
                base.ProcessResults();

                if (this.Attributes.ContainsKey("gpLink")) {
                    this.GPLink = this.Attributes["gpLink"][0].ToString();
                }
                if (this.Attributes.ContainsKey("gpOptions")) {
                    this.GPOptions = this.Attributes["gpOptions"][0].ToString();
                }

                if (!string.IsNullOrWhiteSpace(this.GPLink)) {
                    this.GPLinkGPODNs = ADDataAccess.GetGPLinkGPODNs(this.GPLink);
                    if (this.GPLinkGPODNs.Count == 0) {
                        Debug.WriteLine(string.Format("{0}  [ThreadID: {1}]  {2} DN: {3} Warning: gpLink may contain invalid data: {4}",
                            DateTime.Now.TimeOfDay, Thread.CurrentThread.ManagedThreadId,
                            CustomExtensions.CurrentMethodName(), this.DN, this.GPLink));
                    }
                }

                if (this.Attributes.ContainsKey("instanceType")) {
                    Enum.TryParse<InstanceTypeFlags>(this.Attributes["instanceType"][0].ToString(), result: out instanceType);
                }
                if (this.Attributes.ContainsKey("location")) {
                    this.Location = this.Attributes["location"][0].ToString();
                }
                if (this.Attributes.ContainsKey("siteObjectBL")) {
                    siteObjectBL = ADDataAccess.GetMultiValuedAttribute<string>("siteObjectBL", this.Attributes);
                }

                this.IsValid = !string.IsNullOrWhiteSpace(this.DN)
                && this.ObjectClass.Any(x => string.Equals(x, ObjectCategoryShortName, StringComparison.OrdinalIgnoreCase));
            }
            catch (Exception e) {
                Debug.WriteLine(string.Format("{0}  [ThreadID: {1}]  {2} Error: {3}", DateTime.Now.TimeOfDay, Thread.CurrentThread.ManagedThreadId, CustomExtensions.CurrentMethodName(), e.VerboseExceptionString()));
                throw;
            }
        }
        public string ToFormattedString() {
            var info = new StringBuilder();

            info.AppendLine(string.Format("Site Name: {0}", this.Name ?? "NULL"));
            info.AppendLine(string.Format("Site DN: {0}", this.DN ?? "NULL"));

            return info.ToString();
        }
        #endregion
    }
}
